//
// itertool - a polynormial and type-safe iterator framework in C++.
//
// Copyright (c) 2021-2025 Feng Cong
//
// Distributed under the MIT Software License
// See accompanying file LICENSE.txt or copy at
// https://opensource.org/licenses/MIT

#pragma once

#include <itertool/itertool_fwd.h>

#include <type_traits>
#include <utility>

namespace itertool {

template <typename Traits>
using AdapterItem = typename Traits::ItemType;

namespace detail {

template <typename Traits, typename = void>
struct IteratorAdapterRequires : std::false_type {};

template <typename Traits>
struct IteratorAdapterRequires<
    Traits, std::void_t<decltype(&Traits::HasNext), decltype(&Traits::GetNow),
                        decltype(&Traits::Advance), AdapterItem<Traits>>>
    : std::true_type {};

template <typename IterableCls, typename Traits>
class IteratorAdapter : public Iterable<AdapterItem<Traits>> {
  static_assert(IteratorAdapterRequires<Traits>::value,
                "IteratorAdapter requires Traits to have the following members:"
                "Static member functions: HasNext(), GetNow(), Advance();"
                "Type alias: ItemType");

 public:
  using ItemType = AdapterItem<Traits>;

  template <typename... Args>
  explicit IteratorAdapter(std::in_place_t, Args&&... args)
      : iterable_(std::forward<Args>(args)...) {}

  bool HasNext() override { return Traits::HasNext(iterable_); }
  ItemType GetNow() override { return Traits::GetNow(iterable_); }
  void Advance() override { Traits::Advance(iterable_); }

  bool IsFinite() override { return IsFiniteImpl<Traits>(0); }
  bool IsResetable() override { return IsResetableImpl<Traits>(0); }
  void Reset() override { ResetImpl<Traits>(0); }
  size_t LengthHint() override { return LengthHintImpl<Traits>(0); }

 private:
  template <typename Tr, typename = decltype(&Tr::IsFinite)>
  bool IsFiniteImpl(int) {
    return Tr::IsFinite(iterable_);
  }
  template <typename>
  bool IsFiniteImpl(...) {
    return false;
  }

  template <typename Tr, typename = decltype(&Tr::IsResetable)>
  bool IsResetableImpl(int) {
    return Tr::IsResetable(iterable_);
  }
  template <typename>
  bool IsResetableImpl(...) {
    return false;
  }

  template <typename Tr, typename = decltype(&Tr::Reset)>
  void ResetImpl(int) {
    Traits::Reset(iterable_);
  }
  template <typename>
  void ResetImpl(...) {}

  template <typename Tr, typename = decltype(&Tr::LengthHint)>
  size_t LengthHintImpl(int) {
    return Traits::LengthHint(iterable_);
  }
  template <typename>
  size_t LengthHintImpl(...) {
    return 0;
  }

  IterableCls iterable_;
};

}  // namespace detail

template <typename IterableCls, typename Traits = IterableTraits<IterableCls>,
          typename... Args>
Iterator<AdapterItem<Traits>> MakeIteratorAdapter(Args&&... args) {
  return MakeIterator<detail::IteratorAdapter<IterableCls, Traits>>(
      std::in_place, std::forward<Args>(args)...);
}

}  // namespace itertool
